﻿using System;
using System.ComponentModel;

namespace Mozi.NTP
{

    //TODO 加密验证部分未处理
    //TODO KOD包未处理
    //TODO NTP控制包未实现

    //RFC1305 1992 NTP Version3 
    //RFC5905 2010 NTP Version4 修订 RFC7822,8573,9109  

    //参考RFC NTPv4实现 并向下兼容NTPv3
    //| org  | T1      | origin timestamp   |
    //| rec  | T2      | receive timestamp  |
    //| xmt  | T3      | transmit timestamp |
    //| dst  | T4      | destination timestamp
    //| t    | t       | packet time

    //T(t) = T(t_0) + R(t_0)(t-t_0) + 1/2 * D(t_0)(t-t_0)^2 + e,
    //NTP控制报文
    public class NTPControlPackage
    {

    }
    /// <summary>
    /// 时间戳起点为1900-01-01
    /// 日期最大校准精度为0.1微秒
    /// </summary>
    /// <summary>
    /// 32bit整数+32bits小数/2^32,整数部分正常取值，小数部分/4294967295进行换算
    /// </summary>
    public struct TimeStamp
    {
        /// <summary>
        /// 秒部分
        /// </summary>
        public uint Seconds;
        /// <summary>
        /// 小数部分
        /// </summary>
        public uint Fraction;

        public const long FractionSecondRate = (long)1 + uint.MaxValue;

        public byte[] Pack
        {
            get
            {
                byte[] data = new byte[8];
                Array.Copy(BitConverter.GetBytes(Seconds).Revert(), data, 4);
                Array.Copy(BitConverter.GetBytes(Fraction).Revert(), 0, data, 4, 4);
                return data;
            }
            set
            {
                byte[] data = new byte[8];
                Array.Copy(value, data, value.Length < 8 ? value.Length : 8);
                byte[] di = new byte[4], df = new byte[4];
                Array.Copy(data, di, 4);
                Array.Copy(data, 4, df, 0, 4);
                Seconds = BitConverter.ToUInt32(di.Revert(), 0);
                Fraction = BitConverter.ToUInt32(df.Revert(), 0);
            }
        }

        /// <summary>
        /// 时间设置统一使用UTC+0:00
        /// </summary>
        public DateTime UniversalTime
        {
            get
            {
                if (Seconds == 0 && Fraction == 0)
                {
                    return NTPProtocol.JAN_1970;
                }
                else
                {
                    DateTime dateTimeStart = new DateTime(1900, 1, 1);
                    dateTimeStart = dateTimeStart.AddSeconds(Seconds);
                    //100纳秒=0.1微秒 
                    dateTimeStart = dateTimeStart.AddTicks((long)((double)Fraction / FractionSecondRate * 1e7));
                    return dateTimeStart;
                }
            }
            set
            {
                DateTime dateTimeStart = new DateTime(1900, 1, 1);
                var dtDiff = (value - dateTimeStart);
                Seconds = (uint)dtDiff.TotalSeconds;
                Fraction = (uint)(((double)(dtDiff.Ticks / 1e7) - Seconds) * 1e7 * FractionSecondRate);
            }
        }
        /// <summary>
        /// 转为字符串格式，最多只能到100纳秒
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return UniversalTime.ToString("yyyy-MM-dd HH:mm:ss.FFFFFFF UTC");
        }
    };

    /// <summary>
    /// 短时间格式
    /// 16bits整数+16bit小数/2^16 整数部分正常取值，小数部分/65536进行换算
    /// </summary>
    public struct ShortTime
    {
        public ushort Integer;
        public ushort Fraction { get; set; }

        public byte[] Pack
        {
            get
            {
                byte[] data = new byte[4];
                Array.Copy(BitConverter.GetBytes(Integer).Revert(), data, 2);
                Array.Copy(BitConverter.GetBytes(Fraction).Revert(), 0, data, 2, 2);
                return data;
            }

            set
            {
                byte[] data = new byte[4];
                Array.Copy(value, data, value.Length < 4 ? value.Length : 4);
                byte[] di = new byte[2], df = new byte[2];
                Array.Copy(data, di, 2);
                Array.Copy(data, 2, df, 0, 2);
                Seconds = BitConverter.ToUInt16(di.Revert(), 0);
                Fraction = BitConverter.ToUInt16(df.Revert(), 0);
            }
        }

        public double Seconds
        {
            get
            {
                return Integer + Math.Round((double)Fraction / FractionSecondRate, 4, MidpointRounding.AwayFromZero);
            }
            set
            {
                Integer = (ushort)((ushort)(value * 10) / 10);
                Fraction = (ushort)((value - Integer) * FractionSecondRate);
            }
        }

        public const int FractionSecondRate = ushort.MaxValue + 1;

    }
    /// <summary>
    /// 协议版本
    /// </summary>
    public enum NTPVersion
    {
        Ver1 = 0x01,
        Ver2 = 0x02,
        Ver3 = 0x03,
        Ver4 = 0x04
    }
    /// <summary>
    /// V3版本常量
    /// </summary>
    public class NTPProtocol
    {
        public const int Port = 123;
        /// <summary>
        /// 组播地址
        /// </summary>
        public const string MulticastAddress = "224.0.0.1";

        public static DateTime JAN_1970 = new DateTime(1970, 01, 01);

        /// <summary>
        /// Version Number
        /// </summary>
        public const int Version = 3;
        ///Max Stratum
        public const int MaxStratum = 15;
        ///Max Clock Age in seconds
        public const int MaxAge = 86400;
        ///Max Skew in seconds
        public const int MaxSkew = 1;
        ///Max Distance in seconds
        public const int MaxDistance = 1;
        //V3版本中取值范围为6-10，V4版本中Pool取值范围为4-17
        ///Min Polling Interval power of 2 in seconds
        public const int MinPool = 6;// (64 sec)
        ///Max Polling Interval power of 2 in seconds
        public const int MaxPool = 10;// (1024 sec)
        /// <summary>
        /// 时钟频率 Hz
        /// </summary>
        public const int ClockRate = 1000;
        ///Min Select Clocks
        public const int MinClock = 3;
        ///Max Select Clocks
        public const int MaxClock = 10;
        ///Min Dispersion in seconds
        public const double MinDispersion = 0.01d;
        ///Max Dispersion in seconds
        public const double MaxDispersion = 16d;
        /// Reachability Reg Size
        public const int Window = 8;
        ///Filter Size
        public const int Shift = 8;
        ///Filter Weight
        public const double Filter = 1 / 2;
        ///Select Weight
        public const double Select = 3 / 4;

        public const double PHI = 15e-6;
    }
    /// <summary>
    /// V4版本中的常量
    /// </summary>
    public class NTPProtocolV4:NTPProtocol
    {
        public const int TTLMax = 8;       /* max ttl manycast */
        public const int Beacon = 15;      /* max interval between beacons */
        public const int NSTAGE = 8;       /* clock register stages */
        public const int NMAX = 50;      /* maximum number of peers */
        public const int NSANE = 1;       /* % minimum intersection survivors */
        public const int NMIN = 3;      /* % minimum cluster survivors */
    }

    public enum NTPWorkMode
    {
        [Description("")]
        Undefined = 0,
        ActiveP2P = 1,
        PassiveP2P = 2,
        Client = 3,
        Server = 4,
        Broadcast = 5,
        ControlDatagraph = 6,
        Reserved = 7
    }
}
